#ifndef WIN32
#include "x11_selection.hpp"


#define _DEBUG 1
#undef _DEBUG

/*
 *
 *
Copyright (c) 1989  X Consortium

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Except as contained in this notice, the name of the X Consortium shall not be
used in advertising or otherwise to promote the sale, use or other dealings
in this Software without prior written authorization from the X Consortium.
 * *
 * Author:  Ralph Swick, DEC/Project Athena
 */


// Encryption and decryption of words while pasting. 
// As far as rights are concerned, the same as above applies.
// Author: Francesco Feltrin, 2010


/* xsel.c

   Implements an X selection command line interface. You can
   copy input from stdin to X selection and paste the current
   X selection the stdout.

   ver 0.01 vherva 13082000
   ver 0.02 vherva 14082000 - fixed -display switch, a hang with -p and
                              empty selection and cleaned up a bit.
   ver 0.03 vherva 11062001 - use XSetWMHints to start the program in
                              iconic mode. This workarounds (or fixes,
                              depending on how do you look at it) the
                              orphaned window behaviour some folks were
                              seeing. (You do get an entry to the KDE task
                              bar etc, but that can be seen as useful, hence
                              I'm lazy fix it.)
   ver 0.04 vherva 23072002 - add --selection, --merge, clean up.
   ver 0.04.1 vherva 30112002 Fix a typo in WriteStdout() - thanks to 
                              José Fonseca <j_r_fonseca@yahoo.co.uk>

   This code is entirely based on the XFree86 xcutsel.c.

   To compile:
      gcc xsel.c -O2 -o xsel -lX11 -lXt -lXaw -L/usr/X11R6/lib/

   For X selection documentation, see
      http://www.freedesktop.org/standards/clipboards.txt
      http://www.xfree86.org/~keithp/talks/selection.ps
*/

//static XrmOptionDescRec optionDesc[] = {};

//#define Offset(field) XtOffsetOf(OptionsRec, field)
/*
void xsel_clipboard::syntax(char *call)
{
    fprintf (stderr,
             "xsel 0.04 23072002\n\n"
             "Command line interface to the X selections\n\n"
	     "usage:  %s action [options]\n"
	     "actions:\n"
	     "   -p or --paste\n"
	     "      Paste the current X selection (if any) to stdout and\n"
	     "      and exit.\n"
	     "   -c or --copy\n"
	     "      Copy the input from stdin to the X selection. Note that\n"
	     "      the X selection model has no concept of selection buffer\n"
	     "      on the server. Rather, the client that holds the current\n"
	     "      selection makes it available to the other clients and\n"
	     "      has to wait until someone requests it. Hence, %s forks\n"
	     "      and serves the selection requests until someone else\n"
	     "      claims the selection.\n"
	     "      The copied string can also be given on command line\n"
	     "      rather than through stdin: --copy \"string to copy\".\n"
	     "   -m or --merge\n"
	     "      Same as -c, but merge the input to the current selection\n"
	     "      instead of overwriting it.\n"
	     "options:\n"
	     "   -s SELECTION or --selection SELECTION\n"
	     "      Where SELECTION is either \"PRIMARY\", \"SECONDARY\" or\n"
	     "      \"CLIPBOARD\". Selects which X selection to use. For -p,\n"
	     "      the default is to first try PRIMARY, and if it is empty,\n"
	     "      then SECONDARY and finally CLIPBOARD. For -c and -m, the\n"
	     "      default is PRIMARY.\n",
             call, call);
    exit (1);
}
*/

int xsel_clipboard::read_fd(int fd, char** result)
{
    int len(0), allocated(0);
    *result = NULL;

    while (1)
    {
      int res(0);

      if (len - allocated == 0)
		*result = static_cast<char*>(realloc(*result, allocated += ALLOCCHUNK));

      if (!*result) return -1;

      res = read(fd, &(*result)[len], allocated - len);

      if (!res) return len;
      else if (res < 0) return res;

      len += res;
    }

    return len;
}


int xsel_clipboard::read_stdin(char** result)
{
	return xsel_clipboard::read_fd(0, result);
}


int xsel_clipboard::write_fd(int fd, char* string, int len)
{  
    for (int written = 0; written < len; )
    {
      int res = write(fd, &string[written], len - written);

      if (!res) return len;
      else if (res < 0) return res;

      written += res;
    }

    return len;
}

int xsel_clipboard::write_stdout(char* string, int len)
{
	return xsel_clipboard::write_fd(1, string, len);
}


/* ARGSUSED */
void xsel_clipboard::quit(
	Widget w,
    	XtPointer, //closure          /* unused */
    	XtPointer //callData         /* unused */
	)
{
    XtCloseDisplay( XtDisplay(w) );
    free(tmpbuffer); tmpbuffer =NULL;
    exit(0);
}




void xsel_clipboard::store_buffer(Widget w, XtPointer //client_data
	  , Atom *selection, Atom *type, 
	  XtPointer value, unsigned long *length, int * //format
	  )
{
    if ((*type == 0 || *type == XT_CONVERT_FAIL || *length == 0) &&
        selection_current == 0)
    {
        XtFree(static_cast<char*>(value));
		selection_type = (String)"SECONDARY";
        XmuInternStrings(XtDisplay(w), &selection_type, ONE, selection);
		selection_current = 1;
		XtGetSelectionValue(w, *selection, XA_STRING,
	                    &store_buffer, NULL,
	                    XtLastTimestampProcessed(XtDisplay(w)));
		return;
    }
    else if ((*type == 0 || *type == XT_CONVERT_FAIL || *length == 0) &&
             selection_current == 1)
    {
        XtFree(static_cast<char*>(value));
		selection_type = (String)"CLIPBOARD";
        XmuInternStrings(XtDisplay(w), &selection_type, ONE, selection);
		selection_current = 2;
		XtGetSelectionValue(w, *selection, XA_STRING,
	                    &store_buffer, NULL,
	                    XtLastTimestampProcessed(XtDisplay(w)));
		return;
    }

    if (do_paste)
       write_stdout(static_cast<char*>(value), *length);
    else if (do_merge)
    {
       char* buf = static_cast<char*>(malloc(*length + tmpbuflen + 1));
       if (!buf) quit(w, NULL, NULL);
       bcopy(value, buf, *length);
       buf[*length] = '\0';
       strcat(buf, tmpbuffer);
       free(tmpbuffer);
       tmpbuffer = buf;
       tmpbuflen = strlen(buf);
    }

    XtFree(static_cast<char*>(value));

    if (do_paste)
       quit(w, NULL, NULL);
}


Boolean xsel_clipboard::convert_selection(Widget w, Atom *selection, Atom *target, Atom *type, 
		XtPointer *value, unsigned long *length, int *format)
{
    Display* d = XtDisplay(w);
    XSelectionRequestEvent* req =
        XtGetSelectionRequest(w, *selection, (XtRequestId)NULL);
 
    if((token == NULL))
	return False; // to exit as soon as pasted

   
    if((xsel_clipboard::do_copy || xsel_clipboard::do_merge) && xsel_clipboard::XtSetAppExitFlag) // decrypt as long as exiting 
    {

std::string s(indices.empty() ? token->read() : token->read(indices));

    	if(s.empty()) return False;

    	tmpbuflen = s.length();
    	tmpbuffer = (char*) realloc(tmpbuffer, tmpbuflen+1);
    	strcpy(tmpbuffer, s.c_str());
    }

    xsel_clipboard::XtSetAppExitFlag = !xsel_clipboard::XtSetAppExitFlag;	   

#ifdef _DEBUG
	fprintf(stderr, "[xsel_clipboard::convert_selection] tmpbuflen = %d; tmpbuffer = %s\n", tmpbuflen, tmpbuffer);
#endif

    if (*target == XA_STRING || *target == XA_TEXT(d)) 
    {
        *type = XA_STRING;
        *value = XtMalloc((Cardinal) tmpbuflen + 1);	

	memcpy((char *) *value, tmpbuffer, tmpbuflen);		

        *length = tmpbuflen;
        *format = 8;	
		
        return True;
    }

    if (XmuConvertStandardSelection(w, req->time, selection, target, type,
                                    (XPointer *)value, length, format))
    {
        return True;
    }
	
    return False;
}


void xsel_clipboard::lose_selection(Widget w, Atom * //selection
									)
{
    /* Only remain resident until we lose the selection -
       after that there will be nothing to do. */

	xsel_clipboard::quit(w, NULL, NULL);
}


/* ARGSUSED */
void xsel_clipboard::get_selection(
		Widget w, 
		XtPointer,        // unused
    		XtPointer        // unused
	)
{
   XtGetSelectionValue(w, selection, XA_STRING,
                        store_buffer, NULL,
                        XtLastTimestampProcessed(XtDisplay(w)));
}


/* ARGSUSED */
void xsel_clipboard::get_buffer(
		Widget w, 
		XtPointer, //closure, 
		XtPointer //callData         // unused
	)
{
    if (XtOwnSelection(w, selection,
		       XtLastTimestampProcessed(XtDisplay(w)),
		       convert_selection, lose_selection, NULL))
	{	}
}

/*
int xsel_clipboard::parse_args(int argc, char *argv[])
{
    int i;
    int errs = 0;

    for (i = 1; i < argc; i++)
    {
      if (strncmp(argv[i], "-p", 2) == 0 ||
	  strncmp(argv[i], "--p", 3) == 0)
	do_paste++;
      else if (strncmp(argv[i], "-c", 2) == 0 ||
	       strncmp(argv[i], "--c", 3) == 0)
	do_copy++;
      else if (strncmp(argv[i], "-m", 2) == 0 ||
	       strncmp(argv[i], "--m", 3) == 0)
	do_merge++;
      else if (strncmp(argv[i], "-s", 2) == 0 ||
	       strncmp(argv[i], "--s", 3) == 0)
      {
        i++;
	if (i >= argc)
	  errs = 2;
	else
	{
	  selection_current = -1;
	  if      (tolower(argv[i][0]) == 'p')
	    selection_type = "PRIMARY";
	  else if (tolower(argv[i][0]) == 's')
	    selection_type = "SECONDARY";
	  else if (tolower(argv[i][0]) == 'c')
	    selection_type = "CLIPBOARD";
	  else
	    goto error;
	}
      }
      else
      {
        if (!do_copy && !do_merge) goto error;

        tmpbuflen += strlen(argv[i]) + 1;
        tmpbuffer = realloc(tmpbuffer, tmpbuflen);
	if (!tmpbuffer) return -1;
	if (tmpbuflen == strlen(argv[i])) tmpbuffer[0] = '\0';
	else strcat(tmpbuffer, " ");
	strcat(tmpbuffer, argv[i]);
	tmpbuflen = strlen(tmpbuffer);
      }
    }

    if (do_paste + do_copy + do_merge == 1)
      return 1;

error:
    syntax(argv[0]);
    exit(2);
}
*/

int xsel_clipboard::run()
{
    if (xsel_clipboard::do_copy || xsel_clipboard::do_merge)
    {
        if (tmpbuflen == -1)
          tmpbuflen = read_stdin(&tmpbuffer);
		if (tmpbuflen < 0)
	  	{
	    	perror("Error in read_stdin(): ");
	    	exit(1);
	  	}

	/* The X selection model has no concept of selection buffer
	   on the server. Rather, the client that holds the current
	   selection makes it available to the other clients and
	   has to wait until someone requests it. Hence, we fork
	   and serve the selection requests until someone else
	   claims the selection. (We exit on LoseSelection callback.) */

		if (fork())
		{
			 return 0;
		}
    }
	

    XtAppContext appcon;
    int argc(0);
    const char* argv = "\0";
	
    XtSetLanguageProc(NULL, NULL, NULL);
    Widget shell(XtAppInitialize(&appcon, argv, NULL, 0,
			  &argc, (char**)&argv, NULL, NULL, 0));

    XmuInternStrings(XtDisplay(shell), &selection_type, ONE, &selection);

    /* Somebody please tell me how I can access X selection without creating
	 dummy widget... */

    Widget box(XtCreateManagedWidget(argv, boxWidgetClass, shell, NULL, ZERO));
    XtRealizeWidget(shell);

    XWMHints hints;    
    hints.initial_state = IconicState;
    hints.flags = StateHint;

    XSetWMHints(XtDisplay(shell), XtWindow(shell), &hints);
    
    if (xsel_clipboard::do_paste || xsel_clipboard::do_merge)
    {
	xsel_clipboard::get_selection(shell, NULL, NULL);
    }

    if (xsel_clipboard::do_copy ||xsel_clipboard::do_merge)
    {
	xsel_clipboard::get_buffer(shell, NULL, NULL);
    }

     xsel_clipboard::XtAppMainLoop(appcon);

     return 0;
}


void xsel_clipboard::XtAppMainLoop(const XtAppContext& appContext)
{
	XEvent event;

	while(!xsel_clipboard::XtSetAppExitFlag)
	{
		XtAppNextEvent(appContext, &event);
		XtDispatchEvent(&event);
	}
	
	while(XtAppPending(appContext) !=0)
	{
		XtAppNextEvent(appContext, &event);
		XtDispatchEvent(&event);
	}
}

Atom xsel_clipboard::selection;
XrmOptionDescRec* xsel_clipboard::optionDesc; 	// = NULL;
char* xsel_clipboard::tmpbuffer; 		// = NULL;
int xsel_clipboard::tmpbuflen; 			// = -1;
int xsel_clipboard::selection_current; 		// =0;
int xsel_clipboard::do_paste; 			// =0;
int xsel_clipboard::do_copy; 			// =0;
int xsel_clipboard::do_merge; 			// =0;
String xsel_clipboard::selection_type; 		// = "PRIMARY";
bool xsel_clipboard::XtSetAppExitFlag;

int xsel_clipboard::init()
{
	xsel_clipboard::tmpbuffer=NULL;
	xsel_clipboard::tmpbuflen = 0;	
	xsel_clipboard::selection_type = (String)"PRIMARY";
	xsel_clipboard::selection_current = 0; 
	xsel_clipboard::optionDesc = NULL;
	xsel_clipboard::do_merge = xsel_clipboard::do_copy = xsel_clipboard::do_paste = 0; 
	xsel_clipboard::XtSetAppExitFlag = false;
		
#ifdef _DEBUG	
	printf("[xsel_clipboard::init] tmpbuflen = %d\n", tmpbuflen);
#endif
	
    if(!(tmpbuffer = static_cast<char*>(realloc(tmpbuffer, tmpbuflen+1))))
    {
#ifdef _DEBUG
	fprintf(stderr, "[xsel_clipboard::init] error while reallocating\n");
#endif
		return -1;
	}

	tmpbuffer[0] = '\0'; 	

	return 0;
}

xsel_clipboard::xsel_clipboard(itoken *_itoken):
	clipboard(_itoken)
{
	this->init();
   }	


xsel_clipboard::xsel_clipboard(itoken *_itoken, std::vector<unsigned short> _idx):
	clipboard(_itoken, _idx)
{
	this->init();
   }



int xsel_clipboard::read_buffer()
{
	xsel_clipboard::do_paste++, xsel_clipboard::do_copy=0, xsel_clipboard::XtSetAppExitFlag = false;
	return this->run();
}

int xsel_clipboard::write_buffer()
{
	xsel_clipboard::do_paste=0, xsel_clipboard::do_copy++, xsel_clipboard::XtSetAppExitFlag = false;
	return this->run();
}


int xsel_clipboard::clear_buffer()
{
	return 0;
}

#endif
